﻿
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;

namespace Test30.Core
{
        public class PropertyBag : IEnumerable<PropertyBag>
    {

        public static PropertyBag FromFile(string filePath)
        {
            string[] lines = File.ReadAllLines(filePath, Encoding.ASCII);

            IEnumerable<string> included = PropertyBagParser.ParseIncludes(Path.GetDirectoryName(filePath), lines);
            var enumerable = included as string[] ?? included.ToArray();
            IEnumerable<string> noComments = PropertyBagParser.StripComments(enumerable);
            IEnumerable<string> noWhitespace = PropertyBagParser.StripEmptyLines(noComments);

            /*
            string path = "output.txt";
            FileStream fs = new FileStream(path, FileMode.Create);
            StreamWriter sw = new StreamWriter(fs);

            foreach (string s in enumerable)
            {
                sw.WriteLine(s);
            }

            sw.Flush();
            sw.Close();
            fs.Close();
            */

            IndentationTree tree = IndentationTree.Parse(noWhitespace);

            return PropertyBagParser.Parse(tree);
        }

        public PropertyBag(string name, string value, IEnumerable<PropertyBag> baseProps)
        {
            Name = name ?? throw new ArgumentNullException($"name");
            _value = value ?? throw new ArgumentNullException($"value");

            if (baseProps != null)
            {
                _bases.AddRange(baseProps);
            }
        }

        public PropertyBag(string name, string value) : this(name, value, null) { }
        public PropertyBag(string name, IEnumerable<PropertyBag> baseProps) : this(name, String.Empty, baseProps) { }
        public PropertyBag(string name) : this(name, String.Empty) { }


        public string Name { get; }

        public string Value => _value;
        public int Count => FlattenProperties.Count;

        public void Add(PropertyBag prop)
        {
            _children.Add(prop);
        }

        public bool Contains(string child)
        {
            return FlattenProperties.Contains(child);
        }

        public IEnumerator<PropertyBag> GetEnumerator()
        {
            return FlattenProperties.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public PropertyBag this[string name]
        {
            get
            {
                // try this level
                if (_children.Contains(name)) return _children[name];

                // recurse up the bases (in reverse order so that later items override previous ones)
                for (int i = _bases.Count - 1; i >= 0; i--)
                {
                    PropertyBag baseProp = _bases[i];
                    if (baseProp.Contains(name)) return baseProp[name];
                }

                // not found
                return null;
            }
        }

        private PropSetCollection FlattenProperties
        {
            get
            {
                PropSetCollection properties = new PropSetCollection();

                // start with the parent properties
                foreach (PropertyBag baseProp in _bases)
                {
                    foreach (PropertyBag child in baseProp.FlattenProperties)
                    {
                        if (properties.Contains(child.Name)) properties.Remove(child.Name);
                        properties.Add(child);
                    }
                }

                // override with the child ones
                foreach (PropertyBag child in _children)
                {
                    if (properties.Contains(child.Name)) properties.Remove(child.Name);
                    properties.Add(child);
                }

                return properties;
            }
        }

        private class PropSetCollection : KeyedCollection<string, PropertyBag>
        {
            protected override string GetKeyForItem(PropertyBag item) { return item.Name; }
        }

        public T GetOrDefault<T>(string name, Func<string, T> converter, T defaultValue)
        {
            if (Contains(name)) return converter(this[name].Value);

            return defaultValue;
        }

        public string GetOrDefault(string name, string defaultValue)
        {
            if (Contains(name)) return this[name].Value;

            return defaultValue;
        }

        public int GetOrDefault(string name, int defaultValue)
        {
            if (Contains(name)) return this[name].ToInt32();

            return defaultValue;
        }

        public int ToInt32()
        {
            return Int32.Parse(_value);
        }

        public bool TryGetValue(string name, out PropertyBag value)
        {
            value = this[name];
            return value != null;
        }

        private readonly List<PropertyBag> _bases = new List<PropertyBag>();
        private readonly PropSetCollection _children = new PropSetCollection();
        private readonly string _value;
    }

}
